--
--处理玩家进入匹配场
--

local skynet = require "skynet"
local cluster = require "skynet.cluster"
require "skynet.manager"

local configdao = require "configdao"
local timetool = require "timetool"
local filelog = require "filelog"
local tabletool = require "tabletool"
local list = require "list"

require "sssenum"
require "pubenum"

--匹配桌配置
local matchtable_conf = nil 

--陪练起始rid
local base_rid = 1111111

local CMD = {}
local handler = { }
local service = nil

--匹配桌
local match_table = { 
    idle_table = {},    -- 空闲匹配桌
    full_table = {},    -- 满人匹配桌
}

-- 匹配队列
local Match_queue = {         
    balance_factor = {}, -- 均衡因子，让新入队列元素均衡分布在子队列
}

local check_matchqueue_fork = nil   --检测匹配队列中匹配超时的线程

local wait_rolelist = {}    --等待角色列表：rid-->{roledata = roledata,level = level, curtime = curtime}

local Table_player_num = 2  --桌子开局需要人数
local Min_queue_len = 1     --队列最小长度（满足该长度，才进行匹配,长度为桌子人数的倍数）
local Sub_queue_num = 1     --子队列数
local Match_outtime = 6    --匹配超时时间(超过该时间将强制尝试一次匹配）
local Match_final_outtime = 300 --匹配最终超时时间（超过该时间，将结束匹配）
local Level_queue_num = 6   --不同级别队列数，默认3种
local ROW = 2
local slice = math.ceil(Level_queue_num/ROW)

local function caclate_tip(base_cost, play_game_type, table_type)
    ----自由抢庄 ，服务费，修改成50%，看牌抢庄不变，还是30%
    if table_type == nil then
        if play_game_type == DNQZType.QZ_TYPE_KANPAI then  ---看牌抢庄 
            return math.floor(base_cost * 0.3)
        elseif play_game_type == DNQZType.QZ_TYPE_FREE then  ----自由抢庄
            return math.floor(base_cost * 0.5)
        else
            return math.floor(base_cost * 0.3)
        end
    elseif table_type == SSSTableType.TABLE_TYPE_ZUANSI then
        if play_game_type == DNQZType.QZ_TYPE_KANPAI then  ---看牌抢庄 
            return math.floor(base_cost * 0.3)
        elseif play_game_type == DNQZType.QZ_TYPE_FREE then  ----自由抢庄
            return math.floor(base_cost * 0.5)
        else
            return math.floor(base_cost * 0.3)
        end
    elseif table_type == SSSTableType.TABLE_TYPE_COIN then
        ---友豆场服务费  百分之二十
        return math.floor(base_cost * 0.2)
    end
end

----------------------------------

function CMD.init(caller)
	service = caller -- 这里的caller，是router

    --初始化匹配队列
    Match_queue:init_matchqueue(Level_queue_num)

    --初始化空闲桌
    for i = 1,Level_queue_num do
        match_table.idle_table[i] = { }
    end

    --开启匹配超时检测
    if not check_matchqueue_fork then
        check_matchqueue_fork = skynet.fork(handler.check_match_outtime)
    end

    --初始化匹配桌配置
    handler.init_matchtable_conf()

end

-- function CMD.coinmatch(msg,...)
--     local f = CMD[msg]
--     if not f then
--         return { retcode = EErrCode.ERR_INVALID_REQUEST,retstr = "消息不存在！"}
--     end

--     return f(...)
-- end

-------------------------

-----------------------------
-- 队列扩展接口

--
--初始化匹配队列
--level_num 不同级别队列数
--
function Match_queue:init_matchqueue(level_num)
    level_num = level_num or 3

    --创建不同级别队列
    for i = 1,level_num do
        self[i] = {}
        table.insert(self.balance_factor,0) 
    end

    --每种级别队列，再创建4个子队列
    local sub_num = Sub_queue_num 
    for i = 1,level_num do
        for j = 1,sub_num do
            table.insert(self[i],list:new())
        end
    end
end
--
-- 插入值到匹配队列
-- level 队列级别
-- ret 返回加入队列的索引
--
function Match_queue:push_matchqueue(level, rid)
    local sub = self.balance_factor[level] % (#self[level]) + 1
    self[level][sub]:push_right(rid)
    self.balance_factor[level] = self.balance_factor[level] + 1

    return sub
end
--
-- 从匹配队列删除
-- level队列级别
--
function Match_queue:pop_matchqueue(level, sub)
    return Match_queue.pop_left(self[level][sub])
end

--
-- 从匹配队列删除n个,并插入tmplist
--轮次从每个子队列删除
-- level队列级别
--
function Match_queue:pop_n_matchqueue(level, n,tmplist)
    local num = 0
    local i = 0
    local empty_num = 0

    while true do
        local sub = i % (#self[level]) + 1
        local val = Match_queue.pop_left(self[level][sub])
        i = i + 1
        if val then
            table.insert(tmplist,val)
            num = num + 1
            if num == n then
                return n
            end
        else
            empty_num = empty_num + 1
            if empty_num == #self[level] then
                return num
            end
        end
    end
end

--
--获取最短匹配队列，进行桌子分配处理
--
function Match_queue:get_less_matchqueue(level, sub, less_len)
    less_len = less_len or Min_queue_len

    local real_size = self[level][sub]:get_size() or 0
    if real_size < less_len then
        less_len = real_size - real_size % Table_player_num
    end

    local sub_queue = { }
    for i = 1,less_len do
        table.insert(sub_queue,Match_queue.pop_left(self[level][sub]))
    end

    return sub_queue
end

--
--获得队列总长度
--level 队列级别
--
function Match_queue:get_total_size(level)
    local total_size = 0
    for i,queue in pairs(self[level]) do
        total_size = total_size + queue:get_size()
    end

    return total_size
end

--
--从队列查找
--level 队列级别
--sub 子队列序号
--
function Match_queue:find_from_matchqueue(rid , level, sub)
    local left_dis = -1   --左距离
    local right_dis = -1  --右距离
    local pos = -1        --在队列中位置

    if level and sub then
        local subqueue = self[level][sub]
        local first = subqueue.first
        local last = subqueue.last

        --查找
        while first <= last do
            if subqueue[first] == rid then
                pos = first
                break
            elseif subqueue[last] == rid then
                pos = last
                break
            else
                first = first + 1
                last = last - 1
            end
        end
        if pos ~= -1 then
            left_dis = pos - subqueue.first
            right_dis = subqueue.last - pos
        end
        return pos,left_dis,right_dis
    end
end
--
--从左边开始移动
--first 第一个移动位置
--last 最后一个移动位置
--dis 移动距离,默认1
--
function Match_queue.shifht_from_left(mqueue, first, last, dis)
    dis = dis or 1
    while first >= last do
        mqueue[first + dis] = mqueue[first]
        first = first - 1
    end
    mqueue.first = mqueue.first + 1
end

--
--从右边开始移动
--first 第一个移动位置
--last 最后一个移动位置
--dis 移动距离,默认1
--
function Match_queue.shifht_from_right(mqueue, first, last, dis)
    dis = dis or 1
    while first <= last do
        mqueue[first - dis] = mqueue[first]
        first = first + 1
    end
    mqueue.last = mqueue.last - 1
end

--
--从右边删除
--值为0的，说明是已删除的元素，继续pop下一个
--
function Match_queue.pop_right(mqueue)
    while true do
        local val = mqueue:pop_right()
        if val == nil or val ~= 0 then
            return val
        end
    end
end

--
--从左边删除
--值为0的，说明是已删除的元素，继续pop下一个
--
function Match_queue.pop_left(mqueue)
     while true do
        local val = mqueue:pop_left()
        if val == nil or val ~= 0 then
            return val
        end
    end
end

--
--从匹配队列删除
--在队列两边：直接删除
--在队列中间：移动元素不超过10个时，移动队列并删除；移动元素超过10个时，将其值置为0，后续再处理
--level 队列级别
--
function Match_queue:remove_from_matchqueue(rid, level)
    local subqueue = self[level]
    local left_dis = -1   --左距离
    local right_dis = -1  --右距离
    local pos = -1        --在队列中位置

    wait_rolelist[rid] = nil
    for i,queue in pairs(subqueue) do
        local size = queue:get_size()
        if size > 0 then
            if queue[queue.last] == rid then
                queue:pop_right()
                return true
            end

            if queue[queue.first] == rid then
                queue:pop_left()
                return true
            end

            --在子队列按序查找
            pos,left_dis,right_dis =  Match_queue:find_from_matchqueue(rid, level, i) 
            local move_num = left_dis 
            if pos ~= -1 then
                if right_dis < move_num then
                    move_num = right_dis
                end

                --移动数量不大于1000，移动
                if move_num <= 1000 then
                    if move_num == left_dis then    --左移
                        Match_queue.shifht_from_left(queue,pos - 1,queue.first)
                    else    --右移
                        Match_queue.shifht_from_right(queue,pos + 1,queue.last)
                    end
                    queue.size = queue.size - 1
                else
                    queue[pos] = 0
                    --此时队列长度并没有减1
                end
                return true
            end
        end
    end
    return false
end

----------------------------

local function swap(arr,i,j)
    local tmp = arr[j]
    arr[j] = arr[i]
    arr[i] = tmp
end

--
--初始化匹配桌配置
--
function handler.init_matchtable_conf()
    matchtable_conf = tabletool.deepcopy(configdao.get_common_conf("coinmatchtable_conf"))
end

function handler.reload_match_conf()
    matchtable_conf = tabletool.deepcopy(configdao.get_common_conf("coinmatchtable_conf"))
    ----通知所有匹配桌更新配置
end
--
--通知匹配结果
--
function handler.send_match_result_to_player(rids,message)
    local retcode,retstr,msgbody
    if message and message.retcode  then
		retcode = tonumber(message.retcode)
		retstr = message.retstr or ""
		msgbody = message.msgbody or { }
    else
        msgbody = message
	end
    local rids = rids
    local msgname = "coinmatchresultN"
    local nodename = skynet.getenv("nodename") or "dnnode"
    nodename = string.sub(nodename,1, string.len(nodename) - 4)
	local status = skynet.pcall(cluster.send,"gatenode", ".router", "notify", rids, nodename .. "." ..msgname, msgbody, retcode, retstr)
    if not status then
        filelog.sys_error("notice player failed!")
    end
end

--
--通知gate，开始匹配
--
function handler.notice_gate_startmatch(rid)
    local nodename = skynet.getenv("nodename") or "dnnode"
    nodename = string.sub(nodename,1, string.len(nodename) - 4)
    skynet.pcall(cluster.send, "gatenode", ".router", "role_start_match", rid, nodename)
end

--
--通知gate，结束匹配
--
function handler.notice_gate_endmatch(rid)
    local nodename = skynet.getenv("nodename") or "dnnode"
    nodename = string.sub(nodename,1, string.len(nodename) - 4)
    skynet.pcall(cluster.send, "gatenode", ".router", "role_end_match", rid, nodename)
end
--
--获得空闲匹配桌
--level 匹配桌级别（1初级 2中级 3高级）
-- nums 获得空闲桌数量,默认1
--
function handler.get_idle_matchtable(level,nums)
    nums = nums or 1

    local idletable = { }
    local num = 0
    local index_to_codes = service:get_gamenode_codes()
    for k,v in pairs(match_table.idle_table[level]) do
        match_table.idle_table[level][k] = nil
        if index_to_codes[k] and not match_table.full_table[k] then
            table.insert(idletable,k)
            num = num + 1
            if num >= nums then
                break
            end
        end
    end

    --桌子不够，创建匹配桌，默认创建10个
    if num < nums then
        CMD.create_some_coin_match_table(level,nums - num)
        for k,v in pairs(match_table.idle_table[level]) do
            table.insert(idletable,k)
            num = num + 1
            match_table.idle_table[level][k] = nil
            if num >= nums then
                break
            end
        end
    end
    return idletable
end

----获取所有匹配桌中可以坐下玩家的桌子
---level 匹配桌级别（1初级 2中级 3高级）
--- needplaynums 需要的玩家座位数
function handler.get_cansit_matchtable(level, needplaynums)
    local cansittables = {}
    if needplaynums == 0 or needplaynums == nil then return cansittables end
    local matchcfg = matchtable_conf[level]
    if not matchcfg then return cansittables end

    local index_to_codes = service:get_gamenode_codes()
    local match_list = service:get_match_list()
    for index, matchtable in pairs(match_table.full_table) do
        if matchtable.level == level and level ~= 0 then
            if index_to_codes[matchtable.table_index] and match_list[matchtable.table_index] then
                ---filelog.sys_error("------match_list-------", match_list[matchtable.table_index])
                if match_list[matchtable.table_index].cur_player >= DNTableEnum.DN_TABLE_MIN_PLAYER_NUM and
                    match_list[matchtable.table_index].cur_player < matchcfg.max_player_num then
                    local can_sit_num = matchcfg.max_player_num - match_list[matchtable.table_index].cur_player
                    --filelog.sys_error("----------needplaynums--before---", needplaynums)
                    if can_sit_num >= needplaynums then
                        local sitobj = { table_index = matchtable.table_index, sit_num = needplaynums}
                        table.insert(cansittables, sitobj)
                        needplaynums = 0
                    else
                        local sitobj = { table_index = matchtable.table_index, sit_num = can_sit_num }
                        table.insert(cansittables, sitobj)
                        needplaynums = needplaynums - can_sit_num
                    end
                    --filelog.sys_error("----------needplaynums--af---", needplaynums)
                end
            end
        end
        if needplaynums <=0 then break end
    end
    ---filelog.sys_error("----cansittables------", cansittables, "----level----needplaynums----", level, needplaynums)
    return cansittables
end

--
--检测所有玩家状态（是否已在桌子，是否超时）
--
function handler.check_allplayers_state(level, match_queue)
    local curtime = timetool.get_time()
    local lasttime = nil
    local real_matchqueue = { }

    local enter_diamond = matchtable_conf[level].enter_diamond
    for i,rid in pairs(match_queue) do
        local status,is_error = skynet.pcall(cluster.call,"gatenode",".router","check_role_state",rid,enter_diamond, true)
        if not status then
            filelog.sys_error("call gate error!")
            break
        end

        --不在桌子内，且未超时，才将其加入实际的匹配队列
        if wait_rolelist[rid] ~= nil then
            lasttime = wait_rolelist[rid].curtime
            if not is_error and (curtime - lasttime < Match_final_outtime) then
                table.insert(real_matchqueue,rid)
            else
                --通知gate，结束匹配
                handler.notice_gate_endmatch(rid)
                wait_rolelist[rid] = nil
            end
        end
    end
    
    return real_matchqueue
end

--
--处理一个匹配成功队列
--level 匹配桌级别（1初级 2中级 3高级）
--match_queue 待处理队列
--
function handler.handle_matchqueue(level,match_queue)
    local player_num = Table_player_num
    
    --检测所有匹配玩家状态，踢出无效的玩家
    match_queue = handler.check_allplayers_state(level, match_queue)

    --打乱玩家顺序,玩家多于一桌才进行
    local first = 1
    local last = #match_queue
    if last > player_num then
        for i = first + 1,last do
            swap(match_queue, i, first + math.random(0,i - first))
        end
    end
    local index_to_codes = service:get_gamenode_codes()
    local table_map = service:get_gamenode_data()

    ---先在匹配桌中寻找空位置坐下,如果没有才新建匹配桌
    local can_sit_tables = handler.get_cansit_matchtable(level, last)
    local fail_sit_players = {}
    if #can_sit_tables > 0 then
        for i = 1, #can_sit_tables do
            local table_index = can_sit_tables[i].table_index
            local sit_num = can_sit_tables[i].sit_num
            if index_to_codes[table_index] then
                local table_service = table_map[index_to_codes[table_index].create_table_id].table_service
                local id = table_map[index_to_codes[table_index].create_table_id].table_index
                if sit_num > 0 then
                    local rids = {}
                    for m = 1, sit_num do
                        local rid = table.remove(match_queue)
                        if wait_rolelist[rid] then
                            local roledata = wait_rolelist[rid].roledata
                            table.insert(rids, rid)
                            --通知gate，结束匹配
                            handler.notice_gate_endmatch(rid)
                            --进桌
                            local status, result = skynet.pcall(skynet.call,table_service, "lua", "matchentertable", rid, roledata, {table_index = table_index})
                            ---filelog.sys_error("----be---status----result-----",rid, status, result)
                            if result and result.retcode == nil then
                                ----进桌成功才自动准备和坐下
                                --坐下
                                status, result = skynet.pcall(skynet.call,table_service, "lua", "matchsitdowntable", rid, roledata, { seat_index = 0 })
                                ---filelog.sys_error("-----be----进桌坐下--------", rid, status, result)
                                if result and result.retcode == nil then
                                    ---filelog.sys_error("-----be--坐下成功-------", rid)
                                    --通知客户端匹配结果
                                    service:forward(rid, table_service, id)
                                    handler.send_match_result_to_player({rid},{table_index = table_index})
                                    wait_rolelist[rid] = nil
                                else
                                    ---filelog.sys_error("--be--坐下失败-----rid----", rid)
                                    table.insert(fail_sit_players, rid)
                                end
                            else
                                ---filelog.sys_error("-----be----进桌失败--------", rid)
                                table.insert(fail_sit_players, rid)
                            end
                        end
                    end
                end
            end
        end
    end

    if #fail_sit_players > 0 then
        ---filelog.sys_error("------fail_sit_players-----", fail_sit_players)
        for key, value in ipairs(fail_sit_players) do
            table.insert(match_queue, value)
        end
        fail_sit_players = {}
    end

    --依次加入空闲桌
    last = #match_queue
    -- if last < player_num and last >= DNTableEnum.DN_TABLE_MIN_PLAYER_NUM then
    --     player_num = last
    -- end
    local table_num = math.floor(last / player_num)
    ---filelog.sys_error("------coinmatchtable----", last, player_num, table_num)
    local idletable = handler.get_idle_matchtable(level,table_num)
    table_num = #idletable
    for i = 1,table_num do
        local table_index = table.remove(idletable)
        if not index_to_codes[table_index] then
            filelog.sys_error("handle_matchqueue:error===>", table_index, index_to_codes, idletable)
        end
        local table_service = table_map[index_to_codes[table_index].create_table_id].table_service
        local id = table_map[index_to_codes[table_index].create_table_id].table_index
        local rids = { }
        for j = 1,player_num do
            local rid = table.remove(match_queue)
            ---filelog.sys_error("-----rid----", rid, player_num, wait_rolelist[rid])
            if wait_rolelist[rid] then
                local roledata = wait_rolelist[rid].roledata
                table.insert(rids,rid)
                --通知gate，结束匹配
                handler.notice_gate_endmatch(rid)
                --进桌
                local status, result = skynet.pcall(skynet.call, table_service, "lua", "matchentertable", rid, roledata, {table_index = table_index})
                ---filelog.sys_error("-------status----result-----",rid, status, result)
                if result and result.retcode == nil then
                    ----进桌成功才自动准备和坐下
                    --坐下
                    status, result = skynet.pcall(skynet.call,table_service, "lua", "matchsitdowntable", rid, roledata, { seat_index = 0 })
                    ---filelog.sys_error("---------进桌坐下--------", rid, status, result)
                    if result and result.retcode == nil then
                        ---filelog.sys_error("-------坐下成功-------", rid)
                        --通知客户端匹配结果
                        service:forward(rid, table_service, id)
                        handler.send_match_result_to_player({rid}, {table_index = table_index})
                        wait_rolelist[rid] = nil
                    else
                        ---filelog.sys_error("----坐下失败-----rid----", rid)
                        table.insert(fail_sit_players, rid)
                    end
                else
                    ---filelog.sys_error("---------进桌失败--------", rid)
                    table.insert(fail_sit_players, rid)
                end
            end
        end
        match_table.full_table[table_index] = {table_index  = table_index,level =level}
    end
    if #fail_sit_players > 0 then
        ---filelog.sys_error("------fail_sit_players2-----", fail_sit_players)
        for key, value in ipairs(fail_sit_players) do
            table.insert(match_queue, value)
        end
        fail_sit_players = {}
    end
    --如果匹配队列还有人，尝试一次超时匹配
    if #match_queue > 0 then
        ---handler.handle_all_outtime_match({[level] = match_queue})
        for k = 1,#match_queue do
            local rid = table.remove(match_queue)
            Match_queue:push_matchqueue(level, rid)
        end
    end
end

--
--添加陪练到匹配桌(测试用，加入机器人）
--
function handler.add_peilian_to_matchtable(num,matchlist,level)
    if true then
        return false
    end

    --加入机器人
    local curtime = timetool.get_time()
    for i = 1,num do
        base_rid = base_rid + 1
        local rid = base_rid
        local roledata = {
            info = { 
                rolename = "陪练" .. (rid % 100000), --名字
                level = 0,              --等级
		        logo = "1",             --头像
		        sex = 2,                --性别
                rid = rid,
            },
            money = {
                diamond = 1000000,
                fangka = 1000,
                coin = 100000,
                lock_diamond = 0,
            },
            is_robot == true,
        }

        wait_rolelist[rid] = { roledata = roledata,curtime = curtime,level = level}
        table.insert(matchlist,rid)
    end

    return true
end

--
--获得所有超时匹配
--
function handler.get_all_outime_match(match_queue)
    local out_time = Match_outtime     --匹配超时时间
    local wait_time = Match_outtime    --循环检测线程默认间隔时间
    local curtime = timetool.get_time()
    local outtime_list = { }

    --检测超时匹配
    for i,v in pairs(match_queue) do
        local cur_queue = match_queue[i]
        local size = cur_queue:get_size()

        if size > 0 then
            for j = cur_queue.first,cur_queue.last do
                local rid = cur_queue[j]
                if rid == 0 then
                    cur_queue:pop_left()
                else
                    if wait_rolelist[rid] == nil then
                        cur_queue:pop_left()
                    else
                        local lasttime = wait_rolelist[rid].curtime
                        if curtime - lasttime >= Match_final_outtime then  --超过最终超时时间，踢出匹配队列
                            CMD.cancelcoinmatch(rid)
                        elseif curtime - lasttime >= out_time then --超过10s还未匹配成功，立即处理（加入机器人或陪打）
                            local val = cur_queue:pop_left()
                            table.insert(outtime_list,val)
                        else
                            local w = out_time - (curtime - lasttime)
                            if w < wait_time then
                                wait_time = w
                            end
                            break
                        end
                    end
                end
            end
        end
    end

    return outtime_list,wait_time
end

--
--处理所有超时匹配
--
function handler.handle_all_outtime_match(outtime_list)
    local player_num = Table_player_num
    
    --处理超时匹配
    for i,curlist in pairs(outtime_list) do
        local len =  #curlist
        if len > 0 then
            handler.handle_matchqueue(i,curlist)
        end 
    end

end

--
--检测匹配超时
--超过一定时间仍在匹配队列的玩家，立即对其进行匹配处理
--
function handler.check_match_outtime()
    local less_len = Min_queue_len
    local level_num = Level_queue_num
    local outtime_list = nil
    local wait_time = 0    --循环检测线程默认间隔时间

    while true do
        -- filelog.print("----check-----start---")
        wait_time = Match_outtime
        outtime_list = { }
        --检测每一个子队列，剔除所有超时的匹配
        for i = 1,level_num do
            ---检查子队列里的人数是否大于等于班车人数,如果大于则马上匹配
            for k,queue in pairs(Match_queue[i]) do
                local son_queue_size = queue:get_size()
                ---filelog.sys_error("-----i----",i,"----k----", k, "--son_queue_size---", son_queue_size)
                if son_queue_size >= less_len then
                    local sub_queue = Match_queue:get_less_matchqueue( i, k, son_queue_size)
                    skynet.pcall(handler.handle_matchqueue, i, sub_queue)
                end
            end
            local rids,wt = handler.get_all_outime_match(Match_queue[i])
            outtime_list[i] = rids
            if wait_time > wt then
                wait_time = wt
            end
        end
        --处理超时匹配
        ---filelog.sys_error("outtime_list",outtime_list)
        skynet.pcall(handler.handle_all_outtime_match,outtime_list)
        ---filelog.sys_error("----check-----end---",wait_time)
        --挂起一段时间，然后继续检测
        skynet.sleep(3 * 100)
    end
end

--
--开始匹配桌子
--加入匹配队列，等待处理
--
function handler.start_match_table(rid, roledata, match_type, gps_addr)
    if not rid or not roledata or match_type < 1 or match_type > Level_queue_num then
        return {retcode = EErrCode.ERR_INVALID_PARAMS,retstr = "无效的请求"}
    end
    if matchtable_conf[match_type].table_type == SSSTableType.TABLE_TYPE_ZUANSI then
        --检测金豆是否足够
        local diamond = roledata.money.diamond or 0
        if diamond < matchtable_conf[match_type].enter_diamond then
            return {retcode = EErrCode.ERR_NOT_ENOUGH_DIAMOND, retstr = "金豆数量不足"}
        end
    elseif matchtable_conf[match_type].table_type == SSSTableType.TABLE_TYPE_COIN then
        ---检测金币够不够
        local coin = roledata.money.coin or 0
        ---判断金币够不够扣服务费
        local tip = caclate_tip(matchtable_conf[match_type].base_diamond, matchtable_conf[match_type].play_game_type, matchtable_conf[match_type].table_type)
        if coin < tip then
            return {retcode = EErrCode.ERR_NOT_ENOUGH_DIAMOND, retstr = "金币数量不足"}
        end
        if coin < matchtable_conf[match_type].enter_diamond then
            return {retcode = EErrCode.ERR_NOT_ENOUGH_DIAMOND, retstr = "金币数量不足"}
        end
    end

    local less_len = Min_queue_len --匹配队列最短长度
    local curtime = timetool.get_time()

    --加入匹配队列
    wait_rolelist[rid] = {roledata = roledata, curtime = curtime, level = match_type}
    if gps_addr ~= nil and gps_addr ~= "" then
        wait_rolelist[rid].roledata.gps_addr = gps_addr
    end
    local queue_index = Match_queue:push_matchqueue( match_type, rid)
    local queue_size = Match_queue[match_type][queue_index]:get_size()
    filelog.print("add to queue",queue_index,rid,queue_size)
    
    --通知gate，开始匹配
    handler.notice_gate_startmatch(rid)
    
    --队列人数满足最小要求，则处理
    if queue_size >= 2 then
        local sub_queue = Match_queue:get_less_matchqueue( match_type, queue_index, queue_size)
        skynet.fork(handler.handle_matchqueue, match_type, sub_queue)
    end

    return {timeout_time = curtime + Match_final_outtime}
end

----------------------------------
--外部接口

--
--将桌子加入空闲匹配桌列表
--
function CMD.add_to_idle_coin_match_table(table_index,level)
    match_table.full_table[table_index] = nil
    local index_to_codes = service:get_gamenode_codes()
    if index_to_codes[table_index] then
        match_table.idle_table[level][table_index] = table_index
    else
        match_table.idle_table[level][table_index] = nil
    end
end

function CMD.clear_full_coin_matchtable(table_index)
    match_table.full_table[table_index] = nil
end

function CMD.reload_coinmatch_conf()
    handler.reload_coinmatch_conf()
end

--
--创建匹配场
--
function CMD.create_coin_match_table(level,table_conf,roledata)
    level = level or EMatchType.MATCH_TYPE_PRIMARY
    table_conf = table_conf or matchtable_conf[level]
    if not table_conf then
        filelog.sys_error("create_coin_match_table: config err!")
    end
    if not roledata then
        roledata = {
            info = { 
                rolename = "root",  --名字
                level = 0,          --等级
		        logo = "1",         --头像
		        sex = 2,            --性别
                rid = 22222222,
            },
            money = {
                diamond = 99999999,
                fangka = 9999999,
                coin = 99999999,
                lock_diamond = 0,
             },
            ip_addr = "",
        }
    end
    table_conf.match_type = level
    --[[
        //创建游戏桌
    message createtable {
        required int32 is_public 		= 1;   		// 是否允许陌生人加入
        required int32 table_mode 		= 2;       	// 牌型倍率选择(1 普通模式 2 扫雷模式)
        required int32 play_game_type 	= 3;     	// 玩法选择(1 自由抢庄 2 看牌抢庄)
        required int32 xia_zhu_type	 	= 4;        // 下注倍数(1 小倍, 2 中倍 3 大倍)
        required int32 score_diamond 	= 5;        // 底注
	    required int32 enter_diamond	= 6;		//进桌多少钻石
	    required int32 leave_diamond	= 7;		//离桌多少钻石
	    required int32 table_type 		= 8;		// 创建房间类型(2 自建房卡桌 1 自建钻石桌)
	    required int32 is_open_flush    = 9;		// 是否开启 同花顺牛,葫芦牛,同花牛,顺子牛 1 不开启 2 开启
	    required int32 max_player_num 	= 10;		// 设置玩家人数(默认5人,可选择5人,7人)
    }
    --]]
    local msgbody = {
        is_public = table_conf.is_public,
        table_mode = DNCardTypeMode.CARD_TYPE_MODE_COMMON,
        play_game_type = table_conf.play_game_type,
        xia_zhu_type = DNXiaZhuType.XZ_TYPE_LITTLE,
        score_diamond = table_conf.base_diamond,
        enter_diamond = table_conf.enter_diamond,
        leave_diamond = table_conf.leave_diamond,
        table_type = table_conf.table_type,
        is_open_flush = table_conf.is_open_flush,
        max_player_num = table_conf.max_player_num,
        match_type = level,
    }
    ---filelog.sys_error("------CMD.create_match_table-----", level, table_conf, roledata, msgbody)
    return service.CMD["createtable"](roledata.info.rid, roledata, msgbody)
end

--
--创建一些匹配桌
--tablenum 创建数量
--
function CMD.create_some_coin_match_table(level,tablenum)
    tablenum = (tablenum or 1) * 2
    if tablenum <= 0 then
        return
    end

    local idle_match_table = match_table.idle_table[level]
    local table_conf = matchtable_conf[level]
    for i = 1,tablenum do
        local new_table = CMD.create_coin_match_table(level,table_conf)
        if type(new_table) == type{} and new_table.table_index then
            idle_match_table[new_table.table_index] = new_table.table_index
        else
            filelog.sys_error("create_some_match_table: there is not enough table service!")
        end
    end
end

--
--检测是否为匹配桌
--
function CMD.check_is_coin_matchtable(table_index)
    local index_to_codes = service:get_gamenode_codes()
    if not table_index or table_index < 1 or 
        not index_to_codes[table_index] then
        return false
    end

    if match_table.full_table[table_index] then
        return true
    end

    for i,idle_table in pairs(match_table.idle_table) do
        if idle_table[table_index] then
            return true
        end
    end
    
    return false
end

--
--更新匹配超时时间
--
function CMD.update_coin_match_outtime(new_time)
    if not new_time or type(new_time) ~= "number" or new_time < 3 then
        return 
    end
    Match_outtime = new_time
end

--
--更新最小队列长度
--
function CMD.update_coin_min_queue_len(new_len)
    if not new_len or type(new_len) ~= "number" 
    or new_len < Table_player_num or new_len % Table_player_num ~= 0 then
        return 
    end
    Min_queue_len = new_len
end

--
--获得匹配桌信息
--
function CMD.get_coinmatchlist()
    local matchlist = { }

    for i,conf in pairs( matchtable_conf) do
        matchlist[i] = {
            match_type 	    = i,        --匹配场类型 1初级 2中级 3高级（级别越高底注越高）
	        dizhu  		    = conf.base_diamond,	        --底注
	        ruchang		    = conf.enter_diamond, 	        --入场钻石
	        lichang		    = conf.leave_diamond,	        --离场钻石
	        conf_intro      = tabletool.deepcopy(conf),     --匹配桌配置
	        max_player      = conf.max_player_num,	        --桌子最大人数                         
        }
        --该匹配场当前总人数：游戏中 + 匹配中
        matchlist[i].cur_total_player =  Match_queue:get_total_size(i)
    end

    --获得游戏中人数
    local index_to_codes = service:get_gamenode_codes()
    local table_map = service:get_gamenode_data()
    for k,item in pairs(match_table.full_table) do
        local i = item.level
        if index_to_codes[k] then
            local table_service = table_map[index_to_codes[k].create_table_id].table_service
            local ok, result = pcall(skynet.call, table_service, "lua", "get_tableinfo", k, nil)
            if ok and result then
                if result.cur_player then
                    matchlist[i].cur_total_player = matchlist[i].cur_total_player + result.cur_player
                else
                    matchlist[i].cur_total_player = matchlist[i].cur_total_player + matchlist[i].max_player
                end
            else
                matchlist[i].cur_total_player = matchlist[i].cur_total_player + matchlist[i].max_player
            end
        else
            match_table.full_table[k] = nil
            match_table.idle_table[i][k] = nil
        end
    end
    
    return matchlist
end

--
--重新开始匹配
--
function  CMD.restart_cointable(rids, match_type)
    ---filelog.sys_error("--------CMD.restart_cointable----", rids, match_type)
    if not rids or #rids == 0 then
        return
    end
    --由客户端再次发送匹配请求
    local msgname = "restartcoinmatchN"
    local nodename = skynet.getenv("nodename") or "dnnode"
    nodename = string.sub(nodename,1, string.len(nodename) - 4)

    local msgbody = { match_type = match_type, nodename = nodename}
    local status = skynet.pcall(cluster.call,"gatenode", ".router", "notify", rids, nodename .. "." ..msgname, msgbody)
    if not status then
        filelog.sys_error("notice player failed!")
    end
end

--
--进入友豆匹配桌
--
function CMD.entercoinmatchtable(rid, roledata, request)
    ---filelog.sys_error("-----CMD.entermatchtable------", rid, "-----roledata-----", roledata, request)
    --设置停服标记后，不能进入匹配
    if service:get_server_flag() then
        return {retcode = EErrCode.ERR_INVALID_REQUEST, retstr = "服务器维护中"}
    end

    if wait_rolelist[rid] ~= nil then
        return {retcode = 0,retstr = ""}
    end

    if service:get_role_data(rid) then
        return {retcode = EErrCode.ERR_HAD_IN_TABLE, retstr = "已经在游戏中了！"}
    end

    --开始匹配
    local level = request.match_type or 1
    local gps_addr = request.gps_addr or ""
    local result = handler.start_match_table(rid, roledata, level, gps_addr)
    ----filelog.sys_error("-----------entercointable------", rid, roledata, request, result)
    return result
end

--
--取消友豆匹配
--
function CMD.cancelcoinmatch(rid, roledata, request)
    if service:get_role_data(rid) then
        return {retcode = EErrCode.ERR_HAD_IN_TABLE, retstr = "已经匹配成功，不能取消匹配！"}
    end
    if wait_rolelist[rid] == nil then
        return {retcode = 0, retstr = ""}
    end

    --取消匹配
    local level = wait_rolelist[rid].level
    local result = Match_queue:remove_from_matchqueue(rid, level)
    if not result then
        return {retcode = 0, retstr = ""}
    end

    --通知gate，结束匹配
    handler.notice_gate_endmatch(rid)
    return {}
end

--
--重新进入匹配桌
--
function CMD.reentercoinmatchtable(rid, roledata, request)
    if wait_rolelist[rid] == nil then
        handler.notice_gate_endmatch(rid)
        return {}
    end

    local level = wait_rolelist[rid].level
    --检测金豆是否足够
    local diamond = roledata.money.diamond or 0
    if diamond < matchtable_conf[level].enter_diamond then
        CMD.cancelcoinmatch(rid,roledata, request)
        return {retcode = EErrCode.ERR_INVALID_PARAMS,retstr = "金豆数量不足"}
    end

    --更新roledata
    local responsemsg = { }
    wait_rolelist[rid].roledata = roledata
    --更新gps
    local gps_addr = request.gps_addr or ""
    if gps_addr ~= nil and gps_addr ~= "" then
        wait_rolelist[rid].roledata.gps_addr = gps_addr
    end
    responsemsg.match_type = level
    responsemsg.timeout_time = wait_rolelist[rid].curtime + Match_final_outtime
    return responsemsg
end

--
--获得等待玩家列表
--
function CMD.get_wait_coin_rolelist()
    local result = tabletool.deepcopy(wait_rolelist)
    local matchqueue = { }
    for i = 1,Level_queue_num do
        matchqueue[i] = tabletool.deepcopy(Match_queue[i])    
    end
    return {matchqueue,result}
end

--
--获得匹配场信息
--
function CMD.get_coinmatchinfo()
    local match_infos = {}
    for i,conf in pairs( matchtable_conf) do
        match_infos[i] = { 
            match_level = i,
            base_score = conf.base_diamond,
            enter_score = conf.enter_diamond,
            max_player = conf.max_player_num,
        }
        match_infos[i].wait_num = Match_queue:get_total_size(i)
        match_infos[i].cur_total_player = Match_queue:get_total_size(i)
    end
    --获得游戏中人数
    local index_to_codes = service:get_gamenode_codes()
    local table_map = service:get_gamenode_data()
    for k,item in pairs(match_table.full_table) do
        local i = item.level
        if index_to_codes[k] then
            local table_service = table_map[index_to_codes[k].create_table_id].table_service
            local ok, result = pcall(skynet.call, table_service, "lua", "get_tableinfo", k, nil)
            if ok and result then
                if result.cur_player then
                    match_infos[i].cur_total_player = match_infos[i].cur_total_player + result.cur_player
                else
                    match_infos[i].cur_total_player = match_infos[i].cur_total_player + match_infos[i].max_player
                end
            else
                match_infos[i].cur_total_player = match_infos[i].cur_total_player + match_infos[i].max_player
            end
        end
    end

    return match_infos
end

return CMD

